home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 1997: The Complete Utilities Toolkit / macworld-complete-utilities-1997.iso / Desktop ⁄ Finder / FinderProgressBar 2.0 / FPB.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-03-10  |  18.6 KB  |  573 lines  |  [TEXT/MMCC]

  1. /*****************************************************************************************************
  2. *                                                                                                    *
  3. * FPB.c - Copyright 1993 - 1995 Chris Larson (larson@cs.ucla.edu), All rights reserved               *
  4. *                                                                                                    *
  5. * Source file of a CDEF which mimics the progress bar used in the Finder. This source file and its   *
  6. * compiled derivatives may be freely used within any freeware/shareware/postcardware/beerware/… as   *
  7. * long as you mention my name in your credits. Neither this source nor its compiled derivatives are  *
  8. * in the public domain and may not be use in any form in public domain software. Neither this source *
  9. * nor its compiled derivatives may be used in any form in a commercial product without the expressed,*
  10. * written consent of the author (me).                                                                *
  11. *                                                                                                    *
  12. * Version 2.0 -- March 10, 1995.                                                                     *
  13. *                                                                                                    *
  14. *****************************************************************************************************/
  15.  
  16. #ifndef    powerc
  17. #include <A4Stuff.h>
  18. #endif
  19.  
  20. #include <Palettes.h>
  21. #include "FinderProgressBar.h"
  22. #include "FPB.h"
  23.  
  24. //-----
  25. // Some global variables.
  26. //-----
  27.  
  28. static const RGBColor    gBlackColor = { 0x0000, 0x0000, 0x0000 };
  29. static const RGBColor    gWhiteColor = { 0xFFFF, 0xFFFF, 0xFFFF };
  30. static const RGBColor    gBlueColor =  { 0xCCCC, 0xCCCC, 0xFFFF };
  31. static const RGBColor    gGrayColor =  { 0x4444, 0x4444, 0x4444 };
  32. static const Pattern    gMyGrayPattern = { { 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55 } };
  33.  
  34. //----------------------------------------------------------------------------------------------------
  35. //
  36. // main -- Handle everything but draw messages.
  37. //
  38. //----------------------------------------------------------------------------------------------------
  39.  
  40. pascal long main(short varCode, ControlHandle theControlHandle, short message, long param)
  41. {
  42.     SignedByte    controlRecState;    // Holds the state of the control record’s handle.
  43.     long        returnValue;        // Holds the value returned by the CDEF.
  44.     ControlPtr    theControl;            // Holds a pointer to the control record.
  45.     
  46.     #ifndef    powerc
  47.  
  48.     // ----------
  49.     // Since I use some static global variables I need to set up A4 so they can be accessed
  50.     // properly; only needed on 680x0 systems.
  51.     // ----------
  52.  
  53.     long        oldA4 = SetCurrentA4();
  54.  
  55.     #endif
  56.     
  57.     // ----------
  58.     // Lock down the control record.
  59.     // ----------
  60.     
  61.     controlRecState = HGetState((Handle)theControlHandle);
  62.     HLock((Handle)theControlHandle);
  63.     
  64.     // ----------
  65.     // Initialize the return value and the control pointer (since the record is locked it is safe
  66.     // to use a pointer). The return value is initialized to 0 since most CDEF messages want 0
  67.     // returned.
  68.     // ----------
  69.     
  70.     returnValue = 0;
  71.     theControl = *theControlHandle;
  72.     
  73.     switch (message) {
  74.         
  75.         // ----------
  76.         // Ignore draw messages if the control is not visible.
  77.         // ----------
  78.         
  79.         case drawCntl:
  80.             if ( theControl->contrlVis )
  81.                 BeginDraw(theControlHandle, varCode);
  82.             break;
  83.         
  84.         // ----------
  85.         // Test the control: return kInProgressBarControlPart if the given point is within the
  86.         // control's rectangle, 0 otherwise (the whole bar is one part).
  87.         // ----------
  88.         
  89.         case testCntl:
  90.             if ( PtInRect(*(Point*)(¶m),&(theControl->contrlRect)) )
  91.                 returnValue = kInProgressBarControlPart;
  92.             break;
  93.         
  94.         // ----------
  95.         // Initialize the control: determine if Color Quickdraw exists and should be used. Actually,
  96.         // this simply looks to see if the control’s owning port is a color port. If so, it infers
  97.         // the existence of CQD and uses it. If not, we’re drawing into a B/W port anyway, so who
  98.         // cares if CQD is present?
  99.         // ----------
  100.         
  101.         case initCntl:
  102.             theControl->contrlData = (Handle)(((theControl->contrlOwner->portBits.rowBytes) & kIsColorPort) == kIsColorPort);
  103.             break;
  104.         
  105.         // ----------
  106.         // For the 24-bit mode region calculation message, the high bit of the given region handle
  107.         // must be cleared. Note that I don’t test this bit to determine which region to calculate.
  108.         // Since this control has no indicator (part with a part code >= 129) I will never receive
  109.         // a message asking for indicator region calculation.
  110.         // ----------
  111.         
  112.         case calcCRgns:
  113.             param &= 0x7FFFFFFF;
  114.         
  115.         // ----------
  116.         // Calculate the control’s region.
  117.         // ----------
  118.         
  119.         case calcCntlRgn:
  120.             RectRgn((RgnHandle)param,&(theControl->contrlRect));
  121.             break;
  122.         
  123.         // ----------
  124.         // All other messages do nothing and return 0.
  125.         // ----------
  126.         
  127.         default:
  128.             break;
  129.     }
  130.     
  131.     // ----------
  132.     // Restore the state of the control record’s handle.
  133.     // ----------
  134.     
  135.     HSetState ((Handle)theControlHandle, controlRecState);
  136.     
  137.     // ----------
  138.     // Restore A4 (only on 680x0).
  139.     // ----------
  140.     
  141.     #ifndef    powerc
  142.  
  143.     SetA4(oldA4);
  144.  
  145.     #endif
  146.     
  147.     // ----------
  148.     // We’re outa here.
  149.     // ----------
  150.     
  151.     return returnValue;
  152. }
  153.  
  154. //----------------------------------------------------------------------------------------------------
  155. //
  156. // BeginDraw -- Save off all the stuff we’re going to change, figure out which colors to use (and
  157. //              whether we need to dither) and call the appropriate drawing routine.
  158. //
  159. //----------------------------------------------------------------------------------------------------
  160.  
  161. void BeginDraw(ControlHandle theControlHandle, short varCode)
  162. {
  163.     GrafPtr            savePort;
  164.     Handle            aHandle, bHandle;
  165.     RgnHandle        oldClip;
  166.     ControlPtr        theControl = *theControlHandle;
  167.     long            useColor = (long)(theControl->contrlData);
  168.     Boolean            doDither = false;
  169.     Rect            theBox = theControl->contrlRect;
  170.     PenState        oldPen;
  171.     RGBColor        oldFore, oldBack;
  172.     RGBColor        frameColor = gBlackColor, barColor = gGrayColor, bodyColor = gBlueColor;
  173.     RGBColor        frameDim, barDim, bodyDim, windowBack;
  174.     
  175.     // ----------
  176.     // Set the port to the control’s port. Very probably not needed (since Apple’s CDEFs do not
  177.     // do this) but just to be extra safe…
  178.     // ----------
  179.     
  180.     GetPort(&savePort);
  181.     SetPort(theControl->contrlOwner);
  182.     
  183.     // ----------
  184.     // Save and reset the pen state
  185.     // ----------
  186.     
  187.     GetPenState(&oldPen);
  188.     PenNormal();
  189.     
  190.     // ----------
  191.     // If we are going to draw in color, set up everything for the color environment. Otherwise, the
  192.     // only thing to check is to see if the bar is inactive (and therefore dithered).
  193.     // ----------
  194.     
  195.     if ( useColor ) {
  196.     
  197.         //----------
  198.         // Save the old colors.
  199.         //----------
  200.  
  201.         GetForeColor(&oldFore);
  202.         GetBackColor(&oldBack);
  203.         
  204.         //----------
  205.         // If we’re supposed to use custom colors, retrieve the colors from the color table,
  206.         // overwriting the default colors.
  207.         //----------
  208.  
  209.         if ( varCode == kFPBCustomColorVarCode ) {
  210.  
  211.             GetAuxiliaryControlRecord(theControlHandle,(AuxCtlHandle*)(&bHandle));
  212.             aHandle = (Handle)((**((AuxCtlHandle)bHandle)).acCTable);
  213.  
  214.             frameColor = *FindColorInTable((CCTabHandle)aHandle,cFPBFrameColorID);
  215.             barColor = *FindColorInTable((CCTabHandle)aHandle,cFPBBarColorID);
  216.             bodyColor = *FindColorInTable((CCTabHandle)aHandle,cFPBBodyColorID);
  217.         }
  218.  
  219.         //----------
  220.         // Retrieve the window content color (background color) of the control’s owner.
  221.         //----------
  222.  
  223.         GetAuxWin(theControl->contrlOwner,(AuxWinHandle*)(&bHandle));
  224.         aHandle = (Handle)((**((AuxWinHandle)bHandle)).awCTable);
  225.  
  226.         windowBack = *FindColorInTable((CCTabHandle)aHandle,wContentColor);
  227.  
  228.         //----------
  229.         // If the bar is to be dimmed, check to see if the appropriate dimmed colors exist. If they
  230.         // all exist, we’ll use them to draw. If not all exist, then draw in the regular colors
  231.         // and dither the result to “dim” it. This is for consistency -- either we want the whole
  232.         // thing in dimmed colors or we want the whole thing dithered; not parts of both.
  233.         //----------
  234.  
  235.         if ( theControl->contrlHilite == kControlInactiveControlPart ) {
  236.  
  237.             frameDim = frameColor;
  238.             barDim = barColor;
  239.             bodyDim = bodyColor;
  240.     
  241.             aHandle = (Handle)GetGDevice();
  242.  
  243.             if ( GetGray((GDHandle)aHandle,&windowBack,&frameDim)
  244.                     && GetGray((GDHandle)aHandle,&windowBack,&barDim)
  245.                     && GetGray((GDHandle)aHandle,&windowBack,&bodyDim) ) {
  246.  
  247.                 frameColor = frameDim;
  248.                 barColor = barDim;
  249.                 bodyColor = bodyDim;
  250.  
  251.             } else {
  252.  
  253.                 doDither = true;
  254.             }
  255.         }
  256.  
  257.     } else if ( theControl->contrlHilite == kControlInactiveControlPart ) {
  258.  
  259.         doDither = true;
  260.     }
  261.  
  262.     //----------
  263.     // Draw the bar’s frame. Since this is always the same and will always lie within the control’s
  264.     // rectangle (thus making the clipping region mind the control’s rectangle would have no effect)
  265.     // we can do it here, before mucking with the clipping region.
  266.     //----------
  267.  
  268.     if ( useColor ) {
  269.  
  270.         RGBForeColor(&frameColor);
  271.         RGBBackColor(&windowBack);
  272.     }
  273.  
  274.     if ( doDither )
  275.         PenPat(&gMyGrayPattern);
  276.  
  277.     FrameRect(&theBox);
  278.     InsetRect(&theBox,1,1);
  279.  
  280.     if ( doDither )
  281.         PenNormal();
  282.  
  283.     //----------
  284.     // Save the current port’s clip region and set it to the intersection of the clip region with
  285.     // the newly inset rectangle (so it won’t allow overwriting of the freshly drawn frame).
  286.     //----------
  287.     
  288.     oldClip = NewRgn();
  289.     GetClip(oldClip);
  290.     
  291.     aHandle = (Handle)NewRgn();
  292.     RectRgn((RgnHandle)aHandle,&theBox);
  293.     SectRgn((RgnHandle)aHandle,oldClip,(RgnHandle)aHandle);
  294.     SetClip((RgnHandle)aHandle);
  295.     DisposeRgn((RgnHandle)aHandle);
  296.  
  297.     //----------
  298.     // Figure out if we are supposed to draw the “Barber Pole” or the regular bar. Use the barber pole
  299.     // when the high word of the control’s RfCon is 0x7FFF. In that case, take the frame number from
  300.     // the low word of the RfCon. Frames are numbered from 0 to ( kFPBFrameCount - 1 ), inclusive.
  301.     //----------
  302.  
  303.     if ( HiWord(theControl->contrlRfCon) == 0x7FFF )
  304.  
  305.         DrawBarberPoleBar(theControl,&barColor,&bodyColor);
  306.  
  307.     else
  308.  
  309.         DrawNormalBar(theControl,&barColor,&bodyColor);
  310.  
  311.     //----------
  312.     // Dither the progress bar if we’re supposed to
  313.     //----------
  314.     
  315.     if ( doDither ) {
  316.  
  317.         if ( useColor )
  318.             RGBBackColor(&windowBack);
  319.  
  320.         PenMode(notPatBic);
  321.         PenPat(&gMyGrayPattern);
  322.         PaintRect(&theControl->contrlRect);
  323.     }
  324.  
  325.     // ----------
  326.     // Restore all the stuff we munged: colors (if needed), pen state, clip region, and current port.
  327.     // ----------
  328.     
  329.     if ( useColor ) {
  330.     
  331.         RGBForeColor(&oldFore);
  332.         RGBBackColor(&oldBack);
  333.     }
  334.     
  335.     SetPenState(&oldPen);
  336.     
  337.     SetClip(oldClip);
  338.     DisposeRgn(oldClip);
  339.     
  340.     SetPort(savePort);
  341. }
  342.  
  343. //----------------------------------------------------------------------------------------------------
  344. //
  345. // DrawNormalBar -- Draw the progress bar according to the control settings using the colors given
  346. //                  in the parameters.
  347. //
  348. //----------------------------------------------------------------------------------------------------
  349.  
  350. void DrawNormalBar(ControlPtr theControl, RGBColorPtr barColor, RGBColorPtr bodyColor)
  351. {
  352.     long    useColor = (long)(theControl->contrlData);
  353.     Rect    theBox = theControl->contrlRect;
  354.  
  355.     //----------
  356.     // Set up theBox to draw the progress bar.
  357.     //----------
  358.  
  359.     InsetRect(&theBox,1,1);
  360.     theBox.right = CalculateBarBoundry(theBox.left,theBox.right,theControl);
  361.  
  362.     //----------
  363.     // Set up colors so that the bar will map to black on a 1-bit device; then draw the bar.
  364.     //----------
  365.  
  366.     if ( useColor ) {
  367.  
  368.         RGBForeColor(barColor);
  369.         RGBBackColor(&gWhiteColor);
  370.     }
  371.  
  372.     PaintRect(&theBox);
  373.  
  374.     //----------
  375.     // Now set up theBox to draw the “empty” space not yet filled by the bar.
  376.     //----------
  377.  
  378.     theBox.left = theBox.right;
  379.     theBox.right = theControl->contrlRect.right - 1;
  380.  
  381.     //----------
  382.     // Set up colors so that the space will map to white on a 1-bit device; then draw the space.
  383.     //----------
  384.  
  385.     if ( useColor ) {
  386.  
  387.         RGBForeColor(bodyColor);
  388.         RGBBackColor(&gBlackColor);
  389.  
  390.     } else
  391.         PenMode(patBic);
  392.  
  393.     PaintRect(&theBox);
  394. }
  395.  
  396. //----------------------------------------------------------------------------------------------------
  397. //
  398. // DrawBarberPoleBar -- Draw the correct frame of the “Barber Pole” animation using the given colors.
  399. //
  400. //----------------------------------------------------------------------------------------------------
  401.  
  402. void DrawBarberPoleBar(ControlPtr theControl, RGBColorPtr barColor, RGBColorPtr bodyColor)
  403. {
  404.     long    useColor = (long)(theControl->contrlData);
  405.     Rect    theBox = theControl->contrlRect;
  406.     short    counter, height, frameNumber;
  407.  
  408.     InsetRect(&theBox,1,1);
  409.  
  410.     //----------
  411.     // Set the pen to be the width of one stripe.
  412.     //----------
  413.  
  414.     PenSize(kBarberPoleStripeWidth,1);
  415.  
  416.     //----------
  417.     // Set up the colors. On a 1-bit device, one of these should map to white and the other black. It
  418.     // doesn’t _really_ matter which maps to which as long as they’re different.
  419.     //----------
  420.  
  421.     if ( useColor ) {
  422.  
  423.         RGBForeColor(barColor);
  424.         RGBBackColor(bodyColor);
  425.     }
  426.  
  427.     //----------
  428.     // Compute the horizontal starting point for drawing. Note that each drawing loop draws one
  429.     // stripe of each color (with gray drawn first). This starting point is set far enough to the
  430.     // left to make the first set of stripes drawn hit the lower left corner of the bar.
  431.     //----------
  432.  
  433.     height = theBox.bottom - theBox.top;
  434.     frameNumber = LoWord(theControl->contrlRfCon);
  435.  
  436.     counter = height + frameNumber + 2 * kBarberPoleStripeWidth - 2;
  437.     counter = counter / ( 2 * kBarberPoleStripeWidth );
  438.     counter = theBox.left - counter * 2 * kBarberPoleStripeWidth + frameNumber;
  439.  
  440.     //----------
  441.     // Now back bias the starting location to account for the first increment.
  442.     //----------
  443.  
  444.     counter = counter - kBarberPoleStripeWidth;
  445.  
  446.     //----------
  447.     // Until we would start drawing to the right of the bar, draw a pair of lines, slanting to the
  448.     // right, first of the two in the bar color, second in the fill color.
  449.     //----------
  450.  
  451.     while ( counter < theBox.right ) {
  452.  
  453.         counter += kBarberPoleStripeWidth;
  454.         PenMode(patCopy);
  455.         MoveTo(counter,theBox.top);
  456.         LineTo(counter + height, theBox.bottom - 1);
  457.  
  458.         counter += kBarberPoleStripeWidth;
  459.         PenMode(patBic);
  460.         MoveTo(counter,theBox.top);
  461.         LineTo(counter + height, theBox.bottom - 1);
  462.     }
  463. }
  464.  
  465. //----------------------------------------------------------------------------------------------------
  466. //
  467. // CalculateBarBoundry -- Given the edges of the progress bar and the control’s setting, determine
  468. //                        where the right edge of the progress bar belongs. This calculation
  469. //                        corresponds to the following equation:
  470. //
  471. //                                       ( boxRight - boxLeft ) * ( controlValue - controlMinimum )
  472. //                    result = boxLeft + ----------------------------------------------------------
  473. //                                                  ( controlMaximum - controlMinimum )
  474. //
  475. //                        on the PowerPC architecture, this is implemented in C; while on the 680x0
  476. //                        it is implemented in assembly. This is done because the 68000 does not
  477. //                        support 32-bit multiplies in hardware, 32-bit multiplies are not strictly
  478. //                        necessary (the 16->32 multiply and 32->16 divide will suffice), and the
  479. //                        compiler was not using the mulu.w and divu.w instructions correctly. (Well,
  480. //                        it was using them correctly in the C sense, but that’s not what I wanted
  481. //                        them to do.)
  482. //----------------------------------------------------------------------------------------------------
  483.  
  484. #ifdef    powerc
  485.  
  486. short CalculateBarBoundry(short boxLeft, short boxRight, ControlPtr theControl)
  487. {
  488.     short    result = boxLeft;
  489.     short    min = theControl->contrlMin;
  490.     short    max = theControl->contrlMax;
  491.     short    val = theControl->contrlValue;
  492.     long    top = ( boxRight - boxLeft ) * ( val - min );
  493.     long    bottom = max - min;
  494.  
  495.     if ( bottom != 0 )
  496.         result += top / bottom;
  497.  
  498.     return result;
  499. }
  500.  
  501. #else
  502.  
  503. //----------
  504. // A couple of notes about the assembly language. This method will calculate the correct answer no
  505. // matter what values are given as long as the following conditions hold:
  506. //  (1) boxRight >= boxLeft, and
  507. //  (2) contrlMax >= contrlValue >= contrlMin.
  508. // Both of these should be met in normal circumstances. If these restrictions hold, then the
  509. // differences (both in the numerator and the one in the denominator) will fit into 16-bit unsigned
  510. // integers. That means that I can use the mulu.w and divu.w instructions to perform the multiply
  511. // and divide, instead of requiring the addition of the software library for 32 bit multiplication
  512. // on the 68000 (The software library was twice the size of this code resource last time I checked).
  513. // The only other thing to note is that a division by zero can’t happen. The denominator is zero
  514. // only when contrlMax == contrlMin. If this is the case, then contrlValue == contrlMin and the
  515. // branch before the multiply will be taken, so the division instruction is never reached.
  516. //----------
  517.  
  518.  
  519. asm short CalculateBarBoundry(short boxLeft, short boxRight, ControlPtr theControl)
  520. {
  521.     fralloc                                                // Give ourselves a stack frame
  522.  
  523.     move.w    boxRight,d1                                    // Right edge of the box to d1
  524.     move.w    boxLeft,d0                                    // Left edge of the box to d0
  525.     sub.w    d0,d1                                        // Width of the box to d0
  526.  
  527.     movea.l    theControl,a0                                // Store the control pointer in a0
  528.     move.w    struct (ControlRecord.contrlValue)(a0),d2    // Control’s value to d2
  529.     sub.w    struct (ControlRecord.contrlMin)(a0),d2        // Normalized value to d2
  530.  
  531.     beq        @1                                            // If normalized value == 0, exit. (Note
  532.                                                         //  that d0 already holds left edge)
  533.  
  534.     mulu.w    d2,d1                                        // box width * normalized value to d1
  535.  
  536.     move.w    struct (ControlRecord.contrlMax)(a0),d2        // Control’s max to d2
  537.     sub.w    struct (ControlRecord.contrlMin)(a0),d2        // Normalized maximum to d2
  538.     
  539.     divu.w    d2,d1                                        // width * value / maximum to d1. Note that
  540.                                                         //  divide by zero can’t happen here.
  541.  
  542.     add.w    d1,d0                                        // Offset left edge by amount in d1
  543.  
  544.  @1:
  545.     frfree                                                // Remove the stack frame
  546.     rts                                                    // Outa here...
  547. }
  548.  
  549. #endif
  550.  
  551. //----------------------------------------------------------------------------------------------------
  552. //
  553. // FindColorInTable -- Return a pointer to the RGBColor matching the given id. Return the first entry
  554. //                     in the table if none match. Note that this routine will not cause memory to
  555. //                       move, but since it returns a pointer which references a handle’s block, one
  556. //                     must be sure to lock the handle if the pointer is going to be used in or
  557. //                     after a call that may move memory (like RGBForeColor() or RGBBackColor()).
  558. //
  559. //----------------------------------------------------------------------------------------------------
  560.  
  561. RGBColor* FindColorInTable(CCTabHandle colorTable, short id)
  562. {
  563.     short    counter;
  564.     
  565.     for ( counter = (**colorTable).ctSize; counter != 0; counter-- ) {
  566.     
  567.         if ( (**colorTable).ctTable[counter].value == id )
  568.             break;
  569.     }
  570.     
  571.     return ( &( (**colorTable).ctTable[counter].rgb ));
  572. }
  573.